package com.flute.icrawler.framework.framework;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.flute.tools.timer.IntervalExecutor;
import com.flute.tools.util.collection.InstanceAccumulator;
import com.flute.icrawler.app.processor.AbstractProcessor;
import com.flute.icrawler.config.CrawlConfigUtil;
import com.flute.icrawler.framework.processor.result.CrawlUrlResult;
import com.flute.icrawler.framework.processor.result.IProcessResult;

/**
 * 正在执行的url容器
 * 
 */
public class LocalCrawlUrlPool {
	private static Logger logger = Logger.getLogger(LocalCrawlUrlPool.class);
	private LocalCrawlUrlPoolLogger localCrawlUrlPoolLogger;
	private List<CrawlUrl> localUrlList;
	private ICrawlUrlPool uniformRemoteCrawlUrlPool;
	private InstanceAccumulator accumulator;
	private final int MAX_LOCAL_DEALCOUNT = 300;

	private ProcessorDependency processorDependency;

	private AddUrlToListThread addUrlToListThread = null;

	public synchronized int size() {
		return localUrlList.size();
	}

	public LocalCrawlUrlPool(ICrawlUrlPool uniformRemoteCrawlUrlPool,
			ProcessorDependency processorDependency) {
		this.localUrlList = new ArrayList<CrawlUrl>();
		this.uniformRemoteCrawlUrlPool = uniformRemoteCrawlUrlPool;
		this.accumulator = new InstanceAccumulator();
		this.localCrawlUrlPoolLogger = new LocalCrawlUrlPoolLogger();
		this.localCrawlUrlPoolLogger.setName("LocalCrawlUrlPool Logger");
		// 设置为框架打印周期的5倍
		localCrawlUrlPoolLogger.setInterval(CrawlConfigUtil.getBaseConfig()
				.getFrameworkPrintInterval() * 1000);
		localCrawlUrlPoolLogger.start();
		addUrlToListThread = new AddUrlToListThread(this);
		addUrlToListThread.start();

		this.processorDependency = processorDependency;

	}

	public void check(CrawlUrl url) {
		// long s = System.currentTimeMillis();

		// System.out.println("synch time 1:"
		// + (System.currentTimeMillis() - s));
		if (allProcessed(url) || url.needInterrupt()) {
			CrawlUrlResult result = url.getResult();
			url.resetProcessingInfos();

			for (IProcessResult rt : result.getResults()) {
				url.registerProcessorResult(rt);
			}
			handleUrl(url);
		}
		// System.out.println("handle time 1:"
		// + (System.currentTimeMillis() - s));

		// System.out.println("check time:" + (System.currentTimeMillis() - s));
	}

	private void handleUrl(CrawlUrl url) {

		synchronized (localUrlList) {
			localUrlList.remove(url);
			// addUrlToLocalList();
		}
		
		long time = System.currentTimeMillis();

		// TODO Auto-generated method stub
		url.getResult().handle(url, uniformRemoteCrawlUrlPool);

		logger
				.info("handle time:" + (System.currentTimeMillis() - time)
						+ "ms");
		// CrawlResult crawlResult = (CrawlResult) url.getResultParameter(
		// ParameterKey.RESULT_CRAWL).getValue();
		// long tmpFetchTime = crawlResult.getFetchTime();
		// if (0 != tmpFetchTime) {
		// n++;
		// fetchTime = fetchTime + tmpFetchTime;
		// double s = 1.0 * n / fetchTime;
		// System.out.println(s + ":" + (int) (5000 * s));
		// addUrlToListThread.setCount((int) (5 * 5000 * s));
		// }

		

	}

	public void addUrlToLocalList(int n) {
		try {
			synchronized (localUrlList) {

				if (localUrlList.size() < MAX_LOCAL_DEALCOUNT) {

					List<CrawlUrl> crawlUrls = uniformRemoteCrawlUrlPool
							.nextUrl(n);
					for (int i = 0; i < crawlUrls.size(); i++) {
						if (!localUrlList.contains(crawlUrls.get(i))) {
							crawlUrls.get(i).initProcessorList();
							localUrlList.add(crawlUrls.get(i));
						}

					}

					// localUrlList.addAll(uniformRemoteCrawlUrlPool.nextUrl(n));

				}
			}
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	public void recordFreeProcessor(IProcessor processor) {
		synchronized (accumulator) {
			accumulator.addKey(processor);
		}
	}

	public InstanceAccumulator getUrlAccumulator() {
		InstanceAccumulator acc = new InstanceAccumulator();
		synchronized (localUrlList) {
			for (CrawlUrl url : localUrlList) {
				ProcessorClassList list = calculateNextProcessors(url);
				if (list != null && list.size() > 0) {
					for (int i = 0; i < list.size(); i++) {
						acc.addKey(list.getProcessorKeys().get(i));
					}
				}
			}
		}

		return acc;
	}

	public InstanceAccumulator getFreeProcessorAccumulator() {
		InstanceAccumulator result = new InstanceAccumulator();
		synchronized (accumulator) {
			result = accumulator.clone();
			accumulator.clear();
		}

		return result;
	}

	public boolean isDone() {

		try {
			return !uniformRemoteCrawlUrlPool.hasNext()
					&& !uniformRemoteCrawlUrlPool.isProcessing();
		} catch (RemoteException e) {
			e.printStackTrace();
			return true;
		}

	}

	public CrawlUrl next(AbstractProcessor processor) {
		// logger.info(processor + "I require one lock");
		// long s = System.currentTimeMillis();
		CrawlUrl rurl = null;
		// synchronized (localUrlList) {
		// logger.info(processor + " syn next time:"
		// + (System.currentTimeMillis() - s));
		// logger.info(processor + "I got one lock");

		// long s1 = System.currentTimeMillis();
		synchronized (localUrlList) {
			int size = localUrlList.size();
			for (int i = 0; i < size; i++) {
				ProcessorClassList nextList = calculateNextProcessors(localUrlList
						.get(i));
				if (nextList == null || nextList.size() == 0
						|| localUrlList.get(i).needInterrupt()) {
					continue;
				}
				if (nextList.contains(processor)) {

					rurl = localUrlList.get(i);
				}
			}
		}

		// }

		// logger.info(processor + " local next time:"
		// + (System.currentTimeMillis() - s1));

		// logger.info(processor + "I release one lock");
		return rurl;
	}

	// public List<CrawlUrl> next(AbstractProcessor processor, int n) {
	// List<CrawlUrl> list = new ArrayList<CrawlUrl>();
	// synchronized (localUrlList) {
	// // addUrlToLocalList(n);
	// for (int i = 0; i < localUrlList.size(); i++) {
	//
	// CrawlUrl url = localUrlList.get(i);
	// ProcessorClassList nextList = calculateNextProcessors(url);
	// if (nextList == null || nextList.size() == 0
	// || url.needInterrupt()) {
	// continue;
	// }
	// if (nextList.contains(processor)
	//
	// ) {
	// if (n > 0) {
	// list.add(url);
	// n--;
	// } else {
	// break;
	// }
	//
	// }
	// }
	// }
	//
	// return list;
	// }

	/**
	 * 计算该URL可以做的下一组Processor
	 * 
	 * @return
	 */
	public ProcessorClassList calculateNextProcessors(CrawlUrl url) {

		ProcessorClassList nextProcessors = processorDependency
				.getNextProcessor(url.getProcessedList());

		nextProcessors = nextProcessors.getExcludeClassList(url
				.getProcessingList());

		return nextProcessors;
	}

	public boolean allProcessed(CrawlUrl url) {
		return processorDependency.getAllProcessors().size() == url
				.getProcessedList().size();
	}

	class LocalCrawlUrlPoolLogger extends IntervalExecutor {

		@Override
		public void execute() {
			try {
				logger.info("more url to crawl:"
						+ uniformRemoteCrawlUrlPool.hasNext());
				logger.info("urls are processing:"
						+ uniformRemoteCrawlUrlPool.isProcessing());
				// for (Class<? extends IProcessor> p : accumulator.getKeys()) {
				// logger.info(p.getSimpleName() + " free execution:"
				// + accumulator.getCount(p));
				// }
			} catch (RemoteException e) {
				e.printStackTrace();
			}
			logger.info("local procssing url count:" + localUrlList.size());
		}
	}

	public void setFinished(boolean isFinished) {
		localCrawlUrlPoolLogger.setFinish(isFinished);
	}

}

class AddUrlToListThread extends Thread {
	private LocalCrawlUrlPool crawlUrlPool;

	private static Logger logger = Logger.getLogger(AddUrlToListThread.class);

	private int count = 50;

	public void setCount(int count) {
		this.count = count;
	}

	public AddUrlToListThread(LocalCrawlUrlPool crawlUrlPool) {
		// TODO Auto-generated constructor stub
		this.crawlUrlPool = crawlUrlPool;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while (true) {
			crawlUrlPool.addUrlToLocalList(50);
			// crawlUrlPool.addUrlToLocalList(count + 10);
			logger.info("local procssing url count:" + crawlUrlPool.size());
			logger.info("add local url count" + count);
			try {
				sleep(5000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
